Video games are about learning to make good decisions. I always ejoyed the liberty they offer through the choices available.
Mario Kart was one of my first game I played as a kid, then on SNES and now on Switch. Back then the was was simple between big and heavy donkey kong or the sneaky Toad, now we get to chose the character,but also kart parts : kart frame, tires and glider (yes, mario kart is also about glidding now).
So, Is there any best combination ?
This project recquired to use the libraries listed below :
library(tidyverse)
library(readxl)
library(knitr)
library(kableExtra)
library(skimr)
library(GGally)
library(gtools) # Generate combination
library(patchwork)
library(magick)
library(png)
library(ggridges)
library(plotly)
A quick reddit query I found a dataset (https://docs.google.com/spreadsheets/d/1g7A-38tn9UAIbB2B3sZI-MpILsS3ZS870UTVMRRxh4Q/edit#gid=0) based on in-game experiments filled by Luigi_Fan2’s (https://twitter.com/Luigi_Fan2).
In its annotations, we can see that there are many redundancies in the karts and parts. Please, refer to this table if you want equivalent stats but different skins.
Instead of directly importing data from google drive, I built an excel file to ease up the wrangling process. The modified excel file is downloadable here. I organized the data on several sheet depending on their type (Character, Kart…)
library(readxl)
Char<- read_excel("Mario Kart 8 Deluxe Stat.xlsx",sheet = "Char",col_names = T, col_types = c("text",rep("numeric", 12)))
Kart <- read_excel("Mario Kart 8 Deluxe Stat.xlsx",sheet = "Kart",col_names = T, col_types = c("text",rep("numeric", 12)))
Tire <- read_excel("Mario Kart 8 Deluxe Stat.xlsx",sheet = "Tire",col_names = T, col_types = c("text",rep("numeric", 12)))
Glide <- read_excel("Mario Kart 8 Deluxe Stat.xlsx",sheet = "Glide",col_names = T, col_types = c("text",rep("numeric", 12)))
´Type´ column is added in each dataset before joining all of them :
Char<-Char %>% add_column(Type="Char", .after='ID')
Kart <- Kart %>% add_column(Type="Kart", .after='ID')
Tire <- Tire %>% add_column(Type="Tire", .after='ID')
Glide <- Glide %>% add_column(Type="Glide", .after='ID')
data1 <- full_join(Char, Kart) %>% full_join(Glide) %>% full_join(Tire) #join datatables
Each character and item has its own stats that adds up on 5 jauges :
IMAGE STAT
44 characters 36 Karts 21 Tires 14 Gliders
IMAGE mariokart builder
Let’s take a glimpse at our data. Since, characters have base caracteristics and parts are only modificators (bonus and malus), 2 tables are generated :
data1 %>% filter(Type=="Char") %>% select_if(is.numeric) %>% skim_to_wide() %>% kable("html") %>% kable_styling(bootstrap_options = "striped")
| type | variable | missing | complete | n | mean | sd | p0 | p25 | median | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| numeric | Accel | 0 | 16 | 16 | 3.69 | 0.44 | 3 | 3.25 | 3.75 | 4 | 4.25 | ▃▆▁▃▃▁▇▆ |
| numeric | Handling_Anti-G | 0 | 16 | 16 | 3.8 | 0.73 | 2.5 | 3.25 | 3.75 | 4.31 | 5 | ▃▂▃▇▂▃▃▃ |
| numeric | Handling_Gliding | 0 | 16 | 16 | 3.8 | 0.73 | 2.5 | 3.25 | 3.75 | 4.31 | 5 | ▃▂▃▇▂▃▃▃ |
| numeric | Handling_Land | 0 | 16 | 16 | 3.8 | 0.73 | 2.5 | 3.25 | 3.75 | 4.31 | 5 | ▃▂▃▇▂▃▃▃ |
| numeric | Handling_Water | 0 | 16 | 16 | 3.3 | 0.73 | 2 | 2.75 | 3.25 | 3.81 | 4.5 | ▃▂▃▇▂▃▃▃ |
| numeric | M-turbo | 0 | 16 | 16 | 3.41 | 0.4 | 2.75 | 3.19 | 3.5 | 3.75 | 4 | ▃▃▁▆▇▁▆▃ |
| numeric | Speed_Anti-G | 0 | 16 | 16 | 3.22 | 0.84 | 2 | 2.5 | 3.25 | 3.81 | 4.5 | ▇▅▂▇▅▂▂▇ |
| numeric | Speed_Gliding | 0 | 16 | 16 | 3.97 | 0.84 | 2.75 | 3.25 | 4 | 4.56 | 5.25 | ▇▅▂▇▅▂▂▇ |
| numeric | Speed_Land | 0 | 16 | 16 | 3.47 | 0.84 | 2.25 | 2.75 | 3.5 | 4.06 | 4.75 | ▇▅▂▇▅▂▂▇ |
| numeric | Speed_Water | 0 | 16 | 16 | 3.72 | 0.84 | 2.5 | 3 | 3.75 | 4.31 | 5 | ▇▅▂▇▅▂▂▇ |
| numeric | Traction | 0 | 16 | 16 | 3.59 | 0.41 | 3 | 3.25 | 3.62 | 3.81 | 4.25 | ▃▇▁▃▇▁▃▃ |
| numeric | Weight | 0 | 16 | 16 | 3.19 | 0.85 | 2 | 2.5 | 3.12 | 3.81 | 4.5 | ▇▅▅▅▅▂▂▇ |
Note that :
data1 %>% filter(Type!="Char") %>% select_if(is.numeric) %>% skim_to_wide() %>% kable("html") %>% kable_styling(bootstrap_options = "striped")
| type | variable | missing | complete | n | mean | sd | p0 | p25 | median | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| numeric | Accel | 0 | 27 | 27 | -0.15 | 0.49 | -1 | -0.5 | 0 | 0.25 | 0.75 | ▃▅▇▅▇▇▅▂ |
| numeric | Handling_Anti-G | 0 | 27 | 27 | -0.065 | 0.31 | -0.75 | -0.25 | 0 | 0.12 | 0.5 | ▁▂▁▆▇▁▅▂ |
| numeric | Handling_Gliding | 0 | 27 | 27 | -0.16 | 0.33 | -0.75 | -0.5 | 0 | 0 | 0.25 | ▂▅▁▃▁▇▁▅ |
| numeric | Handling_Land | 0 | 27 | 27 | -0.065 | 0.34 | -0.75 | -0.25 | 0 | 0.25 | 0.5 | ▂▂▁▅▇▁▅▂ |
| numeric | Handling_Water | 0 | 27 | 27 | 0.065 | 0.38 | -0.75 | -0.12 | 0 | 0.25 | 0.75 | ▁▃▃▇▁▇▃▂ |
| numeric | M-turbo | 0 | 27 | 27 | -0.056 | 0.47 | -1 | -0.25 | 0 | 0.25 | 0.75 | ▁▃▂▇▇▇▃▂ |
| numeric | Speed_Anti-G | 0 | 27 | 27 | 0.0093 | 0.31 | -0.75 | -0.12 | 0 | 0.25 | 0.5 | ▁▂▁▃▇▁▅▂ |
| numeric | Speed_Gliding | 0 | 27 | 27 | -0.19 | 0.28 | -0.75 | -0.38 | -0.25 | 0 | 0.25 | ▂▃▁▆▁▇▁▂ |
| numeric | Speed_Land | 0 | 27 | 27 | -0.019 | 0.35 | -0.75 | -0.25 | 0 | 0.25 | 0.5 | ▁▃▁▇▇▁▇▅ |
| numeric | Speed_Water | 0 | 27 | 27 | -0.12 | 0.29 | -0.75 | -0.25 | -0.25 | 0 | 0.5 | ▁▂▁▇▇▁▂▂ |
| numeric | Traction | 0 | 27 | 27 | -0.046 | 0.52 | -1.25 | -0.25 | 0 | 0.25 | 1 | ▂▂▂▅▇▆▃▂ |
| numeric | Weight | 0 | 27 | 27 | -0.0093 | 0.34 | -0.5 | -0.25 | 0 | 0.25 | 0.5 | ▆▆▁▇▁▇▁▅ |
Note that :
For now, mean speed and mean handling are used, we go into details later.
data<- data1 %>%mutate(Speed_mean=rowMeans(data1[grepl("Speed", names(data1))])) %>% mutate(Handling_mean=rowMeans(data1[grepl("Handling", names(data1))]))
data %>% filter(Type!="Char")%>% select(contains("mean")) %>% skim_to_wide() %>% kable("html") %>% kable_styling(bootstrap_options = "striped")
| type | variable | missing | complete | n | mean | sd | p0 | p25 | median | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| numeric | Handling_mean | 0 | 27 | 27 | -0.056 | 0.27 | -0.62 | -0.19 | 0 | 0.16 | 0.44 | ▂▃▁▂▇▂▅▁ |
| numeric | Speed_mean | 0 | 27 | 27 | -0.079 | 0.15 | -0.5 | -0.16 | -0.062 | 0 | 0.19 | ▁▁▁▂▁▇▂▁ |
I generally like to check correlations between variables in a dataset. For this I use the GGally package.
It looks like Most variables are highly correlated :
data %>% filter(Type=="Char") %>% select_if(is.numeric) %>% select(-(matches('Land|Gliding|Water|Anti'))) %>% ggpairs()
A nice way to summarize all these correlations and observe their relations is to plot a parallel coordinates (or sankey chart). Also available in GGally package, that’s or luck !
data %>%
filter(Type=="Char") %>%
select_if(is.numeric) %>%
select(-(matches('Land|Gliding|Water|Anti'))) %>%
select(Weight, Speed_mean, `M-turbo`,Traction,Accel,Handling_mean)%>% #Correlated variables are put close to each others
ggparcoord(scale='globalminmax', groupColumn = "Weight")+
scale_color_viridis_c(direction = -1,begin = 0.3) +
theme(panel.grid.minor.y = element_blank(), panel.grid.major.x=element_line(color="grey60", linetype = 3),axis.ticks = element_blank(), panel.background = element_blank(), axis.text.x = element_text(angle=15), axis.title = element_blank()) + scale_x_discrete(position="top") + labs(y="Value")
As longtime Nintendo fan, I know that the company puts effort into creating immersive experiences and comprehensive environment. Well, ok it’s always somehow weird and cartoony… but my point is that there’s always something players can rely on and feel as “normal” first (physics, storyline, characters feelings …) before acceptiong all the crazy things that are happening around him.
That door into reality for Mario Kart is characters weights and physics : the player expects Bowser to weight more than Toad and thus be less agile while the latter have less inertia (thus lower maximum speed). The maths behind Mario Kart’s game design can be summed with the following chart :
plot_ly(data = (data %>% filter(Type=="Char")), x=~Speed_mean,y=~Accel, label=~ID, color=~`M-turbo`,size=~Weight,title="test", text=~paste("Char :",ID, "<br> Weight :", Weight,"<br> M-Turbo :",`M-turbo`)) %>% hide_colorbar() %>% layout(title="Characters distribution between Acceleration, Mean Speed, Turbo and Weight", subtitle="Light characters have higher Acceleration, Turbo (+ Handling & Traction since positively correlated) and \nlower Mean Speed while heavy characters behave in an opposite way")
It is also true with Kart parts (Here : Tires)
ggplot(data %>% filter(Type=="Tire"),
aes(x=Speed_mean,y=Accel))+
geom_point(aes(size=Weight, fill=`M-turbo`),
pch=21)+
ggrepel::geom_text_repel(aes(label=ID),
min.segment.length = 0.1,
point.padding = unit(1.2, 'lines'),
box.padding = unit(0.6, 'lines'),
force=2)+
viridis::scale_fill_viridis()+
geom_rect(aes(ymin=-Inf, ymax=0, xmin=-Inf, xmax=0),
alpha=0.01,
fill="red")+
geom_rect(aes(ymin=0, ymax=Inf, xmin=0, xmax=Inf),
alpha=0.01,
fill="green")+
labs(
title="Tires distribution between Acceleration, Mean Speed, Turbo and Weight",
subtitle="Ligher Tires grant higher Acceleration, Turbo (+ Handling & Traction since positively correlated) bonus and \n Mean Speed malus while heavy characters behave in an opposite way",
caption= "Some kart parts grants malus only, none grants bonus only")
Weight can be consideredas a player preference depending on the kind of gameplay (or character) they like :
Is there any best combination for characters and kart parts ?
The parameters that need to be optimized (i.e. higher values are the best) are :
Weight is put aside because as we seen, it is not considered as a performance but as type of gameplay or preference.
My first hypothesis was that the best combination would come from :
First, I tried to identify best Characters/Kart parts separatly, but I realised that if I could compute all the combination, finding the best combo would be the best way to get a clean answer
comb <- as_data_frame(permutations(n=length(data1$ID),r=4,v=data1$ID, repeats.allowed = F)) %>%
filter(V1 %in% Char$ID & V2 %in% Kart$ID & V3 %in% Tire$ID & V4 %in% Glide$ID) #Generates all the combinations
#following lines merge extracts stats for each characters or parts in differents tables
names(comb)[1] <- "ID"
comb1 <- inner_join(comb, data, by="ID")
names(comb)[1] <- "V1"
names(comb)[2] <- "ID"
comb2 <- inner_join(comb, data, by="ID")
names(comb)[2] <- "V2"
names(comb)[3] <- "ID"
comb3 <- inner_join(comb, data, by="ID")
names(comb)[3] <- "V3"
names(comb)[4] <- "ID"
comb4 <- inner_join(comb, data, by="ID")
data_combo <- comb1[,6:19]+comb2[,6:19]+comb3[,6:19]+comb4[,6:19]
data_combo <- cbind(comb[,1:4], data_combo)
#Computation of combinations stats
data_combo <- comb1[,6:19]+comb2[,6:19]+comb3[,6:19]+comb4[,6:19]
data_combo <- cbind(comb[,1:4], data_combo)
names(data_combo)[1]<-"Char"
names(data_combo)[2]<-"Kart"
names(data_combo)[3]<-"Tire"
names(data_combo)[4]<-"Glidder"
data_combo$ID <- seq_len(nrow(data_combo))
#Mean Handling and Accelarations are recomputed and a Acceleration / Speed variable is added
data_combo <- data_combo %>%mutate(Speed_mean=rowMeans(data_combo [grepl("Speed", names(data_combo ))])) %>% mutate(Handling_mean=rowMeans(data_combo [grepl("Handling", names(data_combo ))]))
At this point, I decided to add few variables that made sense :
#Land Speed and Handling are weighted (twice more important) for mean calculation since most part of the races happens on regular soil.
data_combo <- data_combo %>%
rowwise() %>%
mutate(Speed_mean=weighted.mean(x=c(Speed_Land,`Speed_Anti-G`, Speed_Water, Speed_Gliding), w=c(2,2,1,1))) %>%
rowwise() %>%
mutate(Handling_mean=weighted.mean(x=c(Handling_Land,`Handling_Anti-G`, Handling_Water, Handling_Gliding), w=c(2,2,1,1)))
data_combo <- data_combo %>% mutate(total_perf=as.numeric(Accel+Speed_mean+Handling_mean+Traction+`M-turbo`))
#Weight categories
data_combo <- data_combo %>% mutate(w_cat = cut(Weight,breaks = c(0,2,3,4,5,6),labels = c("Super Light","Light","Medium","Heavy", "Super Heavy")))#Create Weigh categories
#A Accelration/Speed Ratio is computed
data_combo <- data_combo %>% mutate(A_SRatio= `Accel`/Speed_mean)
library(skimr)
data_combo %>% ungroup() %>%
select(-ID) %>%
select_if(is.numeric) %>%
skim_to_wide() %>%
kable("html") %>%
kable_styling()
| type | variable | missing | complete | n | mean | sd | p0 | p25 | median | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| numeric | A_SRatio | 0 | 8064 | 8064 | 1.13 | 0.55 | 0.2 | 0.72 | 1.02 | 1.42 | 4.18 | ▅▇▅▂▁▁▁▁ |
| numeric | Accel | 0 | 8064 | 8064 | 3.4 | 0.83 | 1 | 2.75 | 3.5 | 4 | 5.75 | ▁▂▇▇▇▇▂▁ |
| numeric | Handling_Anti-G | 0 | 8064 | 8064 | 3.53 | 0.84 | 1 | 3 | 3.5 | 4.25 | 5.75 | ▁▁▆▆▆▇▂▁ |
| numeric | Handling_Gliding | 0 | 8064 | 8064 | 3.5 | 0.85 | 1 | 3 | 3.5 | 4 | 5.75 | ▁▂▆▆▇▇▂▁ |
| numeric | Handling_Land | 0 | 8064 | 8064 | 3.61 | 0.85 | 1 | 3 | 3.75 | 4.25 | 5.75 | ▁▁▅▆▆▇▃▁ |
| numeric | Handling_mean | 0 | 8064 | 8064 | 3.54 | 0.81 | 1.12 | 2.96 | 3.54 | 4.12 | 5.67 | ▁▂▅▇▇▆▃▁ |
| numeric | Handling_Water | 0 | 8064 | 8064 | 3.43 | 0.85 | 1 | 2.75 | 3.5 | 4 | 5.75 | ▁▂▇▇▇▇▂▁ |
| numeric | M-turbo | 0 | 8064 | 8064 | 3.35 | 0.8 | 1 | 2.75 | 3.25 | 4 | 5.75 | ▁▂▇▇▇▇▂▁ |
| numeric | Speed_Anti-G | 0 | 8064 | 8064 | 3.37 | 0.93 | 0.75 | 2.75 | 3.25 | 4 | 5.75 | ▁▂▅▇▆▆▂▁ |
| numeric | Speed_Gliding | 0 | 8064 | 8064 | 3.43 | 0.92 | 1 | 2.75 | 3.5 | 4.25 | 5.75 | ▁▂▇▆▆▇▃▁ |
| numeric | Speed_Land | 0 | 8064 | 8064 | 3.36 | 0.96 | 0.75 | 2.75 | 3.25 | 4 | 5.75 | ▁▃▅▇▅▆▂▁ |
| numeric | Speed_mean | 0 | 8064 | 8064 | 3.37 | 0.86 | 1.38 | 2.67 | 3.38 | 4.04 | 5.21 | ▁▅▇▇▇▇▆▂ |
| numeric | Speed_Water | 0 | 8064 | 8064 | 3.33 | 0.92 | 1 | 2.5 | 3.25 | 4 | 5.75 | ▁▃▇▆▆▇▂▁ |
| numeric | total_perf | 0 | 8064 | 8064 | 16.97 | 2.1 | 10.38 | 15.54 | 17.04 | 18.5 | 22.58 | ▁▁▃▆▇▆▃▁ |
| numeric | Traction | 0 | 8064 | 8064 | 3.31 | 0.84 | 0.75 | 2.75 | 3.25 | 4 | 5.75 | ▁▂▃▇▆▆▁▁ |
| numeric | Weight | 0 | 8064 | 8064 | 3.18 | 0.98 | 0.75 | 2.5 | 3.25 | 4 | 5.75 | ▁▃▅▇▅▆▂▁ |
ggplot(data_combo, aes(Speed_mean, Accel, label=paste(round(total_perf,2),round(Speed_mean,2), round(Accel,2),"\n",Char,"+",Kart,"+", Tire,"+", Glidder)))+
geom_point(pch=21, alpha=0.95, aes(fill=`M-turbo`, size=Handling_mean))+
viridis::scale_fill_viridis(direction=1)+
geom_abline(intercept=0, slope=1,size=1.5)+
geom_vline(xintercept=4)+
geom_hline(yintercept=4)+
ggrepel::geom_label_repel(data=(data_combo %>% filter(Speed_mean >= 4 & Accel >= 4)%>% top_n(3,total_perf)), aes(x=Speed_mean, y=Accel,label=paste(round(total_perf,2),round(Speed_mean,2), round(Accel,2),"\n",Char,"+",Kart,"+", Tire,"+", Glidder)), size=2.6, segment.colour = "black",force=9,point.padding = unit(0.5, "lines"), box.padding = 0, color="black", fill="white", alpha=0.85, segment.size = 1, nudge_y = 0.5)+
facet_wrap(~w_cat)
Best_Over_Combo <- data_combo %>% filter(Speed_mean >= 4 & Accel >= 4)%>% top_n(5,total_perf) %>% select(ID, Char, Kart, Tire, Glidder)
ggplot(gather((data_combo %>% filter(ID %in% Best_Over_Combo$ID)),key = Stat, value=Stat_v, c(Speed_mean,Speed_Land:Speed_Gliding)),aes(paste(Char, Kart,Tire,Glidder), Stat_v))+geom_col(aes(fill=paste(Char, Kart,Tire,Glidder)))+facet_grid(Stat~., scales = "free")+theme(axis.text.y = element_blank(), legend.position = "bottom", legend.text = element_text(size=7), axis.ticks.y = element_blank(), legend.direction = "vertical", legend.title.align = 0.5)+guides(fill=guide_legend(ncol=3))+labs(fill="Combo", x="", y="Scores")+scale_fill_brewer(type = "qual")
Problem here is that Speed_Land is low. Speen_mean was compensated by the High Speed_Water
ggplot(gather(data_combo %>% filter(Accel>=4 & Speed_mean>5& Handling_mean>4) %>% top_n(1, total_perf),key = Stat, value=Stat_v, c(Speed_Land:Speed_Gliding,Accel, Weight, Handling_Land:Handling_Gliding, Traction, `M-turbo`)),aes(Stat, Stat_v))+geom_col()+coord_flip()
data_combo %>%filter(Char=="Peach", Speed_Land >3) %>% select(Char, Kart, Tire, Glidder,total_perf,Weight, Accel, Speed_mean,Speed_Land, Handling_mean, `M-turbo`,Traction, Handling_Land) %>% arrange(desc(total_perf)) %>% top_n(10, Accel)
## # A tibble: 20 x 13
## Char Kart Tire Glidder total_perf Weight Accel Speed_mean Speed_Land
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Peach W 25… Stan… Super … 19.4 2.75 4. 3.50 3.25
## 2 Peach W 25… Stan… Wario … 19.1 3.00 4. 3.54 3.25
## 3 Peach W 25… Off-… Cloud … 19.0 2.75 4. 3.50 3.25
## 4 Peach Land… Off-… Super … 18.8 2.75 4. 3.21 3.25
## 5 Peach Blue… Roll… Super … 18.8 2.00 4. 3.33 3.25
## 6 Peach Cat … Stan… Super … 18.8 3.00 4. 3.46 3.25
## 7 Peach Stan… Stan… Cloud … 18.7 2.75 4. 3.50 3.25
## 8 Peach W 25… Off-… Peach … 18.7 3.00 4. 3.46 3.25
## 9 Peach Land… Off-… Wario … 18.6 3.00 4. 3.25 3.25
## 10 Peach Blue… Roll… Wario … 18.5 2.25 4. 3.38 3.25
## 11 Peach Blue… Butt… Cloud … 18.5 1.75 4. 3.46 3.25
## 12 Peach Cat … Stan… Wario … 18.5 3.25 4. 3.50 3.25
## 13 Peach Pipe… Off-… Super … 18.5 3.00 4. 3.17 3.25
## 14 Peach Cat … Off-… Cloud … 18.4 3.00 4. 3.46 3.25
## 15 Peach Stan… Stan… Peach … 18.4 3.00 4. 3.46 3.25
## 16 Peach Stan… Butt… Super … 18.3 2.50 4. 3.38 3.25
## 17 Peach Pipe… Off-… Wario … 18.2 3.25 4. 3.21 3.25
## 18 Peach Blue… Butt… Peach … 18.2 2.00 4. 3.42 3.25
## 19 Peach Stan… Butt… Wario … 18.1 2.75 4. 3.42 3.25
## 20 Peach Cat … Off-… Peach … 18.0 3.25 4. 3.42 3.25
## # ... with 4 more variables: Handling_mean <dbl>, `M-turbo` <dbl>,
## # Traction <dbl>, Handling_Land <dbl>
ggplot(data_combo %>%filter(Char=="Peach"), aes(Speed_mean, Accel))+
geom_point(pch=21, alpha=0.85, aes(fill=`M-turbo`, size=Handling_mean))+
ggrepel::geom_label_repel(data=(data_combo %>%filter(Char=="Peach", Speed_mean >= 3 & Accel >= 3) %>% top_n(2, total_perf)), aes(x=Speed_mean, y=Accel,label=paste(total_perf,"\n",Kart,"+", Tire,"+", Glidder,"\n", w_cat)), size=2.6, segment.colour = "black",force=5,point.padding = unit(0.5, "lines"), box.padding = 1.5, color="black", fill="white", alpha=0.87, direction = "y")+
scale_fill_distiller(direction=1, palette="Greens")
cols_fill <- c("Baby Mario" = "#F50000",
"Baby Peach" = "#F4B1D5",
"Baby Rosalina" = "#FFFF8E",
"Bowser" = "#F9B40E",
"Cat peach" = "#FCF7F6",
"Koopa Troopa" = "#E8B500",
"Luigi" = "#289913",
"Mario" = "#D30000",
"Metal Mario" = "#C5C5C5",
"Peach" = "#D39884",
"Rosalina" = "#FFFBDF",
"Tanooki Mario" = "#8A4300",
"Toad" = "#FDF4F3",
"Toadette" = "#F67CC2",
"Waluigi" = "#430C90",
"Wario" = "#FFF300"
)
cols_col <- c("Baby Mario" = "#FFE4BF",
"Baby Peach" = "#FEF459",
"Baby Rosalina" = "#ECC69A",
"Bowser" = "#FE0A00",
"Cat peach" = "#FCF7F6",
"Koopa Troopa" = "#E8B500",
"Luigi" = "#289913",
"Mario" = "#D30000",
"Metal Mario" = "#C5C5C5",
"Peach" = "#D39884",
"Rosalina" = "#FFFBDF",
"Tanooki Mario" = "#8A4300",
"Toad" = "#FE0A00",
"Toadette" = "#F67CC2",
"Waluigi" = "#430C90",
"Wario" = "#FFF300"
)
ggplot(data_combo %>% filter(Char=="Toad"), aes(Speed_mean, Accel))+
geom_point(pch=21, size=2, aes(alpha=`M-turbo`, size=Handling_mean, fill=Char))+
geom_abline(slope=1)
Plot with images
Plot with transparent image (limited loop)
MarioK_img <- image_read("Mario Kart.png")
image_info(MarioK_img)
## format width height colorspace matte filesize density
## 1 PNG 1678 957 sRGB TRUE 1601520 37x37
for (j in c(rep(752,1), rep(820,1), rep(882,1))) {
for(i in c((0:24),(0:24),(0:11))) {
frame <- paste("60x60+",i*65+2,"+",j, sep="")
name <- paste("img",i,j,sep="_")
assign(x = name,value = image_crop(MarioK_img, frame)) %>%image_write(.,path = paste("icons/img",i,j,".png", sep="_"), format="png")
m <- readPNG(paste("icons/img",i,j,".png",sep="_"), F)
w <- m
w[,,4] <- m[,,4]*0.6 # adjust alpha
writePNG(w, paste(paste("icons/img",i,j,".png", sep="_")))
}
}
## Error in file(con, "wb"): impossible d'ouvrir la connexion